package com.wisenet.wx.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSONObject;
import com.wisenet.common.WxConst;
import com.wisenet.entity.WechatUser;
import com.wisenet.pojo.jssdkpojo.Config;
import com.wisenet.pojo.jssdkpojo.Ticket;
import com.wisenet.pojo.menupojo.AccessToken;
import com.wisenet.pojo.menupojo.Menu;
import com.wisenet.util.TencentOssUtil;

/**
 * 公众平台通用接口工具类
 * @author fzh
 *
 */
public class WeixinUtil {

	private static final String OPENID = "openid";
	private static final String TICKET = "ticket";
	private static final String ERRCODE = "errcode";
	private static final String EXPIRES_IN = "expires_in";
	private static final String ACCESS_TOKEN = "access_token";
	private static final Logger logger = Logger.getLogger(WeixinUtil.class);
	
	/**
	 * 发起https请求并获取结果
	 * @param requestUrl 请求地址
	 * @param requestMethod 请求方式(GET/POST)
	 * @param param 提交的数据
	 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
	 * java.lang.ClassCastException: sun.net.www.protocol.http.HttpURLConnection cannot be cast to javax.net.ssl.HttpsURLConnection
	 */
	public static JSONObject httpRequest(String requestUrl, String requestMethod, JSONObject param) {
		InputStream inputStream = null;
		OutputStream outputStream = null;
		BufferedReader bufferedReader = null;
		HttpsURLConnection httpUrlConn = null;
		InputStreamReader inputStreamReader = null;
		try {
			// 创建SSLContext对象，并使用我们指定的信任管理器初始化
			TrustManager[] tm = { new MyX509TrustManager() };
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());
			// 从上述SSLContext对象中得到SSLSocketFactory对象
			SSLSocketFactory ssf = sslContext.getSocketFactory();

			URL url = new URL(requestUrl);
			httpUrlConn = (HttpsURLConnection) url.openConnection();
			httpUrlConn.setSSLSocketFactory(ssf);

			httpUrlConn.setDoOutput(true);
			httpUrlConn.setDoInput(true);
			httpUrlConn.setUseCaches(false);
			
			// 设置请求方式(GET/POST)
			httpUrlConn.setRequestMethod(requestMethod);
			if ("GET".equalsIgnoreCase(requestMethod)) httpUrlConn.connect();

			// 当有数据需要提交时
			if (null != param) {
				outputStream = httpUrlConn.getOutputStream();
				// 注意编码格式，防止中文乱码
				outputStream.write(param.toString().getBytes("UTF-8"));
				outputStream.close();
			}
			
			// 生成小程序二维码
			if (null != param && param.containsKey("page") && param.containsKey("scene")) {
				// 上传图片到腾讯云
				String key = "ocrweb/" + param.getString("scene") + ".jpg";
				TencentOssUtil tCloud = new TencentOssUtil();
	            String qrcodeUrl = tCloud.uploadImage(key, httpUrlConn.getContentLength(), httpUrlConn.getInputStream());
	            JSONObject qrcodeResult = new JSONObject();
	            qrcodeResult.put("qrcodeUrl", qrcodeUrl);
				return qrcodeResult;
			}

			// 将返回的输入流转换成字符串
			inputStream = httpUrlConn.getInputStream();
			inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
			bufferedReader = new BufferedReader(inputStreamReader);

			String str = null;
			StringBuffer buffer = new StringBuffer();
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}
			// 字符串转成JSON
			return JSONObject.parseObject(buffer.toString());
		} catch (ConnectException ce) {
			logger.error("Weixin server connection timed out.");
		}  catch (Exception ex) {
			logger.error("Exception: " + ex);
		} finally {
            try {
            	if (bufferedReader != null) bufferedReader.close();
            	if (inputStreamReader != null) inputStreamReader.close();
                if (inputStream != null) inputStream.close();
            } catch (IOException ex) {
            	logger.error("IOException: " + ex);
            }
			httpUrlConn.disconnect();
		}
		return null;
	}

	/**
	 * 配置jssdk-Config
	 * @param appid
	 * @param appsecret
	 * @param url
	 * @param ticket
	 * @return
	 */
	public static Config setConfig(String appid,String appsecret,String url,String ticket) {
		// 字典排序的签名参数
		String string1 = "jsapi_ticket=%s&noncestr=%s&timestamp=%s&url=%s";
		// 随机字串、时间戳
		String nonceStr = SignUtil.getNonceStr(16), timestamp = SignUtil.getTimeStamp();
		string1 = String.format(string1, ticket, nonceStr, timestamp, url);
		// 对string1进行sha1签名，得到signature
		String signature = SignUtil.md5(string1).toLowerCase();
		// 设置配置信息
		Config config = new Config();
		config.setAppId(appid);
		config.setTicket(ticket);
		config.setNonceStr(nonceStr);
		config.setSignature(signature);
		config.setTimestamp(timestamp);
		return config;
	}

	/**
	 * 小程序登录获取信息
	 * @param jscode
	 * @param appId
	 * @param secret
	 * @return
	 */
	public static JSONObject getOpenIdAndSessionKey(String jscode, String appId, String secret) {
		String requestUrl = String.format(WxConst.JSCODE2SESSION_URL, appId, secret, jscode);
		return httpRequest(requestUrl, "POST", null);
	}
	
	/**
	 * 获取普通access_token
	 * @param appid 凭证
	 * @param appsecret 密钥
	 * @return
	 */
	public static AccessToken getAccessToken(String appid, String appsecret) {
		AccessToken accessToken = null;

		String requestUrl = String.format(WxConst.ACCESS_TOKEN_URL, appid, appsecret);
		JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
		// 如果请求成功
		if (null != jsonObject) {
			if (0 != jsonObject.getIntValue(ERRCODE)) {
				logger.error("获取TOKEN失败: " + jsonObject);
				return null;
			}
			try {
				accessToken = new AccessToken();
				accessToken.setToken(jsonObject.getString(ACCESS_TOKEN));
				accessToken.setExpiresIn(jsonObject.getIntValue(EXPIRES_IN));
			} catch (Exception e) {
				accessToken = null;
				// 获取token失败
				logger.error("获取TOKEN失败: " + e.getMessage());
			}
		}
		return accessToken;
	}
	
	/**
	 * 获取网页授权access_token
	 * @param appid 凭证
	 * @param appsecret 密钥
	 * @param code 网页授权code
	 * @return
	 */
	public static AccessToken getAccessToken(String appid, String appsecret, String code) {
		AccessToken accessToken = null;

		String requestUrl = String.format(WxConst.OAUTH2_ACCESS_TOKEN_URL, appid, appsecret, code);
		JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
		// 如果请求成功
		if (null != jsonObject) {
			try {
				if (0 != jsonObject.getIntValue(ERRCODE)) {
					logger.error("获取TOKEN失败: " + jsonObject);
					return null;
				}
				accessToken = new AccessToken();
				accessToken.setOpenid(jsonObject.getString(OPENID));
				accessToken.setToken(jsonObject.getString(ACCESS_TOKEN));
				accessToken.setExpiresIn(jsonObject.getIntValue(EXPIRES_IN));
			} catch (Exception e) {
				accessToken = null;
				// 获取token失败
				logger.error("获取TOKEN失败: " + e.getMessage());
			}
		}
		return accessToken;
	}
	
	/**
	 * 获取jsapi_ticket（jsapi_ticket的有效期为7200秒）
	 * @param token
	 * @return
	 */
	public static Ticket getTicket(String token) {
		Ticket ticket = null;
		String requestUrl = String.format(WxConst.JSAPI_TICKET_URL, token);
		JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
		// 如果请求成功
		if (null != jsonObject) {
			if (0 != jsonObject.getIntValue(ERRCODE)) {
				logger.error("获取TICKET失败: " + jsonObject);
				return null;
			}
			try {
				ticket = new Ticket();
				ticket.setTicket(jsonObject.getString(TICKET));
				ticket.setExpiresIn(jsonObject.getIntValue(EXPIRES_IN));
			} catch (Exception e) {
				ticket = null;
				// 获取ticket失败
				logger.error("获取TICKET失败: " + e.getMessage());
			}
		}
		return ticket;
	}
	
	/**
	 * QQ小程序获取access_token
	 * @param appid
	 * @param appsecret
	 * @return
	 */
	public static AccessToken getAccessTokenQQ(String appid, String appsecret) {
		AccessToken accessToken = null;

		String requestUrl = String.format(WxConst.ACCESS_TOKEN_URL_QQ, appid, appsecret);
		JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
		// 如果请求成功
		if (null != jsonObject) {
			if (0 != jsonObject.getIntValue(ERRCODE)) {
				logger.error("获取TOKEN失败: " + jsonObject);
				return null;
			}
			try {
				accessToken = new AccessToken();
				accessToken.setToken(jsonObject.getString(ACCESS_TOKEN));
				accessToken.setExpiresIn(jsonObject.getIntValue(EXPIRES_IN));
			} catch (Exception e) {
				accessToken = null;
				// 获取token失败
				logger.error("获取TOKEN失败: " + e.getMessage());
			}
		}
		return accessToken;
	}
	
	/**
	 * 创建菜单
	 * @param menu 菜单实例
	 * @param accessToken 有效的access_token
	 * @return 0表示成功，其他值表示失败
	 */
	public static int createMenu(Menu menu, String accessToken) {
		int result = 0;

		// 拼装创建菜单的url
		String url = WxConst.MENU_CREATE_URL.replace("ACCESS_TOKEN", accessToken);
		// 将菜单对象转换成json字符串
		JSONObject jsonMenu = (JSONObject) JSONObject.toJSON(menu);
		// 调用接口创建菜单
		JSONObject jsonObject = httpRequest(url, "POST", jsonMenu);

		if (null != jsonObject) {
			if (0 != jsonObject.getIntValue(ERRCODE)) {
				result = jsonObject.getIntValue(ERRCODE);
				logger.error("创建菜单失败: " + jsonObject);
			}
		}
		return result;
	}
	
	/**
	 * 创建菜单
	 * @param jsonMenu
	 * @param accessToken
	 */
	public static int createMenu(String jsonMenu, String accessToken) {
		logger.info("jsonMenu = " + jsonMenu);
		int result = 0;
		// 拼装创建菜单的url
		String url = WxConst.MENU_CREATE_URL.replace("ACCESS_TOKEN", accessToken);
		// 调用接口创建菜单
		JSONObject jsonObject = httpRequest(url, "POST", JSONObject.parseObject(jsonMenu));

		if (null != jsonObject) {
			if (0 != jsonObject.getIntValue(ERRCODE)) {
				result = jsonObject.getIntValue(ERRCODE);
				logger.error("创建菜单失败: " + jsonObject);
			}
		}
		return result;
	}
	
	/**
	 * 创建个性化菜单
	 * @param jsonMenu
	 * @param accessToken
	 * @return
	 */
	public static int createConditionalMenu(String jsonMenu, String accessToken) {
		logger.info("jsonMenu = " + jsonMenu);
		int result = 0;
		// 拼装创建菜单的url
		String url = WxConst.CONDITIONAL_MENU_CREATE_URL + accessToken;
		// 调用接口创建菜单
		JSONObject jsonObject = httpRequest(url, "POST", JSONObject.parseObject(jsonMenu));
		
		logger.info("jsonObject = " + jsonObject);
		
		if (null != jsonObject) {
			if (0 != jsonObject.getIntValue(ERRCODE)) {
				result = jsonObject.getIntValue(ERRCODE);
				logger.error("创建菜单失败: " + jsonObject);
			}
		}
		return result;
	}
	
	/**
	 * 创建二维码
	 * @param appid
	 * @param secret
	 */
	public static void createQrcode(String appid,String secret) {
		String ticket = "";
		// action_name （QR_SCENE为临时整型值，QR_STR_SCENE为临时字符串值，QR_LIMIT_SCENE为永久整型值，QR_LIMIT_STR_SCENE为永久字符串值）
		String json = "{\"expire_seconds\": 1800, \"action_name\": \"QR_SCENE\", \"action_info\": {\"scene\": {\"scene_id\": 123}}}";
		try {
			AccessToken at = getAccessToken("wxf8f7e3df26af1fe5", "76ff3e3996bb0cffeb201b7d6e71181b");
			if (at != null) {
				String token = at.getToken();
				logger.info("token = " + token);
				JSONObject jsonObject = httpRequest(WxConst.CREATE_QRCODE_TOKEN_URL.replaceAll("TOKEN", token), "POST", JSONObject.parseObject(json));
				if (jsonObject != null) {
					ticket = jsonObject.getString(TICKET);
					logger.info("ticket = " + ticket);
					logger.info("查看二维码URL：" + WxConst.SHOW_QRCODE_URL + ticket);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 获取网页授权链接
	 * @param appid
	 * @param redirectURI
	 * @param scope
	 */
	public static String getOauth2Url(String appid, String redirectURI, String scope) {
		String oauth2Url = null;
		try {
			oauth2Url = String.format(WxConst.OAUTH2_AUTHORIZE_URL, appid, URLEncoder.encode(redirectURI, "utf-8"), scope);
			logger.info("url = " + oauth2Url);
			return oauth2Url;
		} catch (Exception e) {
			logger.error("getOauth2Url error: " + e.getMessage());
			return null;
		}
	}
	
	/**
	 * 获取标签
	 * @param token
	 */
	public static JSONObject getTags(String token) {
		JSONObject jsonObject = httpRequest(WxConst.GET_TAGS_URL + token, "GET", null);
		if (null != jsonObject) {
			if (0 != jsonObject.getIntValue(ERRCODE)) {
				logger.error("获取标签失败：" + jsonObject);
			}
		}
		return jsonObject;
	}
	
	/**
	 * 获取标签用户
	 * @param token
	 */
	public static JSONObject getTagUsers(JSONObject json, String token) {
		JSONObject jsonObject = httpRequest(WxConst.GET_TAG_USERS_URL + token, "GET", json);
		if (null != jsonObject) {
			if (0 != jsonObject.getIntValue(ERRCODE)) {
				logger.error("获取标签用户失败：" + jsonObject);
			}
		}
		return jsonObject;
	}

	/**
	 * 创建标签
	 * @param json
	 * @param token
	 * @return 0表示成功，其他值表示失败 
	 */
	public static int createTags(JSONObject json, String token) {
		JSONObject jsonObject = httpRequest(WxConst.CREATE_TAGS_URL + token, "POST", json);
		if (null != jsonObject) {
			if (0 != jsonObject.getIntValue(ERRCODE)) {
				logger.error("创建标签失败：" + jsonObject);
				return jsonObject.getIntValue(ERRCODE);
			}
		}
		return 0;
	}
	
	/**
	 * 编辑标签
	 * @param json
	 * @param token
	 * @return 0表示成功，其他值表示失败
	 */
	public static int updateTags(JSONObject json, String token) {
		JSONObject jsonObject = httpRequest(WxConst.UPDATE_TAGS_URL + token, "POST", json);
		if (null != jsonObject) {
			if (0 != jsonObject.getIntValue(ERRCODE)) {
				logger.error("编辑标签失败：" + jsonObject);
				return jsonObject.getIntValue(ERRCODE);
			}
		}
		return 0;
	}
	
	/**
	 * 删除标签
	 * @param json
	 * @param token
	 * @return 0表示成功，其他值表示失败 
	 */
	public static int deleteTags(JSONObject json, String token) {
		JSONObject jsonObject = httpRequest(WxConst.DELETE_TAGS_URL + token, "POST", json);
		if (null != jsonObject) {
			if (0 != jsonObject.getIntValue(ERRCODE)) {
				logger.error("删除标签失败：" + jsonObject);
				return jsonObject.getIntValue(ERRCODE);
			}
		}
		return 0;
	}
	
	/**
	 * 批量为用户打标签
	 * @param json
	 * @param token
	 * @return 0表示成功，其他值表示失败
	 */
	public static int batchUserTags(JSONObject json, String token) {
		logger.info("开始为用户打标签：" + json);
		JSONObject jsonObject = httpRequest(WxConst.BATCH_TAGGING_URL + token, "POST", json);
		if (null != jsonObject) {
			if (0 != jsonObject.getIntValue(ERRCODE)) {
				logger.error("为用户打标签失败：" + jsonObject);
				return jsonObject.getIntValue(ERRCODE);
			}
		}
		logger.info("为用户打标签" + (jsonObject.getIntValue(ERRCODE) == 0 ? "成功" : "失败"));
		return 0;
	}
	
	/**
	 * 获取用户信息
	 * @param token
	 * @param openid
	 * @return
	 */
	public static WechatUser getUserInfo(String token, String openid) {
		WechatUser userInfo = null;
		JSONObject json = httpRequest(String.format(WxConst.USERINFO_URL, token, openid), "GET", null);
		if (json != null && json.getIntValue(ERRCODE) == 0) {
			userInfo = new WechatUser();
			userInfo.setOpenId(openid);
			userInfo.setCreateTime(new Date());
			userInfo.setCity(json.getString("city"));
			userInfo.setGender(json.getInteger("sex"));
			userInfo.setCountry(json.getString("country"));
			userInfo.setNickName(json.getString("nickname"));
			userInfo.setProvince(json.getString("province"));
			userInfo.setAvatarUrl(json.getString("headimgurl"));
		} else {
			logger.error("获取用户信息失败：" + json);
		}
		return userInfo;
	}

}